package com.biglead.orderservice.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.biglead.common.config.Constant;
import com.biglead.common.pojo.GoodsInfo;
import com.biglead.common.pojo.OrderInfo;
import com.biglead.common.pojo.OrderMessage;
import com.biglead.common.pojo.UserInfo;
import com.biglead.feign.clients.FeignUserClient;
import com.biglead.orderservice.config.SnowFlakeCompone;
import com.biglead.orderservice.mapper.OrderMapper;
import com.biglead.orderservice.service.GoodsInfoService;
import com.biglead.orderservice.service.OrderService;
import com.biglead.orderservice.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderInfo> implements OrderService {

    @Resource
    private FeignUserClient feignUserClient;

    @Resource
    private GoodsInfoService goodsInfoService;
    @Autowired
    SnowFlakeCompone snowFlakeCompone;


    /**
     * 查询订单详情
     *
     * @param id
     */
    @Override
    public OrderInfo queryById(Long id) {
        OrderInfo orderInfo = this.getById(id);
        if (orderInfo == null) {
            throw new RuntimeException("未查询到订单");
        }
        Long userId = orderInfo.getUserId();
        //根据用户ID来查询用户信息
        UserInfo userInfo = feignUserClient.queryById(userId);
        orderInfo.setUserInfo(userInfo);
        return orderInfo;
    }

    private Lock lock = new ReentrantLock();

    /**
     * 创建订单
     *
     * @param goodsId 商品ID
     * @param userId  用户ID
     * @return
     * @throws Exception
     */
    @Override
    @Transactional
    public OrderInfo createWrongOrder(Long goodsId, Long userId) throws Exception {

        OrderInfo orderInfo = null;
        // 数据库校验库存
        GoodsInfo goodsInfo = checkStock(goodsId);
        // 扣库存(无锁)
        boolean b = saleStock(goodsInfo);
        if (!b) {
            throw new RuntimeException("库存不足 减库存失败");
        }
        log.info("扣库存完成");
        // 生成订单
        orderInfo = createOrder(goodsInfo, userId);
        log.info("下单完成");

        return orderInfo;
    }

    private GoodsInfo checkStock(Long sid) throws Exception {
        //查询商品详情
        GoodsInfo goodsInfo = goodsInfoService.getGoodsInfoById(sid);
        log.info("当前库存 {}", goodsInfo.getGoodsStock());
        if (goodsInfo.getGoodsStock() < 1) {
            throw new RuntimeException("库存不足");
        }
        return goodsInfo;
    }

    /**
     * 更新库存
     *
     * @param goodsInfo
     * @return
     */
    private boolean saleStock(GoodsInfo goodsInfo) {
        goodsInfo.setGoodsSale(goodsInfo.getGoodsSale() + 1);
        goodsInfo.setGoodsStock(goodsInfo.getGoodsStock() - 1);
        return goodsInfoService.updateStockById(goodsInfo);
    }

    /**
     * 生成订单
     *
     * @param goodsInfo
     * @param userId
     * @return
     * @throws Exception
     */
    private OrderInfo createOrder(GoodsInfo goodsInfo, Long userId) throws Exception {
        Long goodsId = goodsInfo.getId();
        if (redisService.hasKey(Constant.GOODS_INFO + goodsId)) {
            GoodsInfo goodsInfo1 = (GoodsInfo) redisService.get(Constant.GOODS_INFO + goodsId);
            goodsInfo = goodsInfo1;
        }

        OrderInfo order = new OrderInfo();
        //雪花算法 订单号
        order.setSn(snowFlakeCompone.getInstance().nextId() + "");
        order.setGoodsId(goodsInfo.getId());
        order.setUserId(userId);
        order.setNum(1);
        order.setPrice(goodsInfo.getGoodsPrice());
        order.setName(goodsInfo.getGoodsName());
        order.setCreateTime(new Date());
        boolean res = this.save(order);
        if (!res) {
            throw new RuntimeException("创建订单失败");
        }
        return order;
    }

    /**
     * ---------------------------------------------------------------------------------------------
     * redis 分布式事务
     */
    @Resource
    RedisService redisService;

    /**
     * 乐观锁更新库存和Redis
     *
     * @param goodsId 商品ID
     * @param userId  用户ID
     * @return
     * @throws Exception
     */
    @Override
    public OrderInfo createOrderWithLimitAndRedis(Long goodsId, Long userId) throws Exception {
        // 校验库存，从 Redis 中获取
        GoodsInfo goodsInfo = checkStockWithRedis(goodsId);
        // 乐观锁更新库存和Redis
        saleStockOptimsticWithRedis(goodsInfo);
        // 创建订单
        OrderInfo res = createOrder(goodsInfo, userId);
        return res;
    }

    @Override
    public void consumerTopicToCreateOrderWithKafka(OrderMessage orderMessage) throws Exception {
        Long goodsId = orderMessage.getGoodsId();
        Long userId = orderMessage.getUserId();
        String sn = orderMessage.getSn();
        Integer num = orderMessage.getNum();
        // 校验库存，从 Redis 中获取
        GoodsInfo goodsInfo = checkStockWithRedis(goodsId);
        // 乐观锁更新库存和Redis
        saleStockOptimsticWithRedis(goodsInfo);
        // 创建订单

        GoodsInfo goodsInfo1 = (GoodsInfo) redisService.get(Constant.GOODS_INFO + goodsId);



        OrderInfo order = new OrderInfo();

        order.setSn(sn);
        order.setGoodsId(goodsId);
        order.setUserId(userId);
        order.setNum(num);
        order.setPrice(goodsInfo1.getGoodsPrice().multiply(new BigDecimal(num)));
        order.setName(goodsInfo1.getGoodsName());
        order.setCreateTime(new Date());
        order.setOrderStatues(0);
        boolean res = this.save(order);
        if (!res) {
            throw new RuntimeException("创建订单失败");
        }

        //设置过期时间30分钟
        //未支付取消订单
        redisService.expire(Constant.ORDER_SN+goodsId+":"+userId+":"+sn,60*30);
        //缓存订单信息
        redisService.set(sn,order);

        log.info("Kafka 消费 Topic 创建订单成功");


    }

    // Redis 中校验库存
    private GoodsInfo checkStockWithRedis(Long goodsId) throws Exception {
        int count = Integer.parseInt(redisService.get(Constant.GOOOD_STOCK + goodsId).toString());
        int sale = Integer.parseInt(redisService.get(Constant.GOOOD_SALE + goodsId).toString());
        int version = Integer.parseInt(redisService.get(Constant.GOOOD_VERSION + goodsId).toString());
        String goodsName = redisService.get(Constant.GOODS_NAME + goodsId).toString();
        if (count < 1) {
            log.info("库存不足");
            throw new RuntimeException("库存不足 Redis currentCount: " + sale);
        }
        GoodsInfo stock = new GoodsInfo();
        stock.setId(goodsId);
        stock.setGoodsStock(count);
        stock.setGoodsSale(sale);
        stock.setVersion(version);
        stock.setGoodsName(goodsName);

        return stock;
    }


    // 更新 DB 和 Redis
    private void saleStockOptimsticWithRedis(GoodsInfo goodsInfo) throws Exception {
        boolean res = goodsInfoService.updateStockByOptimistic(goodsInfo);
        if (!res) {
            throw new RuntimeException("并发更新库存失败");
        }
        // 更新 Redis
        redisService.updateStockWithRedis(goodsInfo);
    }

}
